package com.huawei.boss.xml;

import java.util.ArrayList;
import java.util.List;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 使用递归来实现
 * @author c00170351
 *
 */
public class RecursiveXmltransStrategy extends DomXmltransStrategy {
    /**
     * 从xml转换成列表,使用递归来实现
     * @param root 待转换的报文根节点
     * @return 返回转换好的列表信息
     */
    @Override
    public List<XmlElem> parseRootNode(Element root) {
        List<XmlElem> xmlElems = new ArrayList<XmlElem>();
        parseNode(0, -1, root, xmlElems);
        return xmlElems;
    }

    /**
     * 从列表转换成xml,使用递归来实现
     * @param xmlElems 待转换的列表信息，并假设已经根据currSeq进行了排序
     * @return 返回xml报文
     */
    public String list2xml(List<XmlElem> xmlElems) {
        StringBuffer sb = new StringBuffer();
        appendElem(0, xmlElems, sb);
        return sb.toString();
    }

    /**
     * 解析某个列表项为报文内容的递归算法
     *
     * @param currSeq
     *            当前解析节点在列表中的序号
     * @param sb
     *            当前已经解析完成的报文内容
     * @param xmlElems
     *            待处理的解析节点列表
     * @return 下一个待处理的节点在列表中的序号
     */
    private int appendElem(int currSeq, List<XmlElem> xmlElems, StringBuffer sb) {
        int nextIdx = currSeq + 1;
        XmlElem xmlElem = xmlElems.get(currSeq);

        //对每个元素，总是先输出开始节点
        sb.append("<" + xmlElem.getTagName() + ">");

        //这里统一处理内容节点和普通节点，简化处理
        sb.append(xmlElem.getTagTxt());
        int total = xmlElems.size();
        while (nextIdx < total) {
            XmlElem nextElem = xmlElems.get(nextIdx);
            if (nextElem.getParentSeq() != xmlElem.getCurrSeq()) {
                break;
            }
            nextIdx = appendElem(nextIdx, xmlElems, sb);
        }

        //对每个元素，最后总是输出结束节点
        sb.append("</" + xmlElem.getTagName() + ">");
        return nextIdx;
    }

    /**
     * 解析某个节点的递归算法
     *
     * 如果合并内容节点判断和子节点递归逻辑的话，是可以简化代码的，但是输出的顺序有点乱，所以不采用
     *
     * @param currSeq
     *            当前解析节点的序号
     * @param parentSeq
     *            当前解析节点的父节点
     * @param currNode
     *            当前解析节点
     * @param xmlElems
     *            已经解析过的节点
     * @return 处理当前节点后的下一个序号
     */
    private int parseNode(int currSeq, int parentSeq, Node currNode, List<XmlElem> xmlElems) {
        String tagName = currNode.getNodeName();
        NodeList childNodes = currNode.getChildNodes();
        int length = childNodes.getLength();
        //判断是否是内容节点
        boolean textOnly = true;

        // 处理子结点
        for (int i = 0; i < length; i++) {
            Node node = childNodes.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                textOnly = false;
                break;
            }
        }

        int nextNodeSeq = currSeq + 1;
        if (textOnly) {
            String content = currNode.getTextContent();
            xmlElems.add(new XmlElem(currSeq, parentSeq, tagName, content));
            return nextNodeSeq;
        }

        // 非空节点且非内容节点，先增加自身节点，然后递归子结点进行处理
        xmlElems.add(new XmlElem(currSeq, parentSeq, tagName, ""));
        for (int i = 0; i < length; i++) {
            Node node = childNodes.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                nextNodeSeq = parseNode(nextNodeSeq, currSeq, node, xmlElems);
            }
        }
        return nextNodeSeq;
    }
}
